// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <pollux/dwio/common/filter_node.h>
#include <pollux/common/exception/exception.h>

namespace kumo::pollux::dwio::common {
    class MetricsLog {
    public:
        static constexpr std::string_view LIB_VERSION_STRING{"1.1"};
        static constexpr melon::StringPiece WRITE_OPERATION{"WRITE"};

        enum class MetricsType {
            HEADER,
            FOOTER,
            FILE,
            STRIPE,
            STRIPE_INDEX,
            STRIPE_FOOTER,
            STREAM,
            STREAM_BUNDLE,
            GROUP,
            BLOCK,
            TEST
        };

        virtual ~MetricsLog() = default;

        // read path logging methods
        virtual void logRead(
            uint64_t duration, // milliseconds
            const std::string &category,
            uint64_t fileSize,
            uint64_t psSize,
            uint64_t footerSize,
            uint64_t readOffset,
            uint64_t readSize,
            MetricsType type,
            uint32_t numFileRead,
            uint32_t numStripeCache) const {
        }

        struct ReadMetrics {
            uint64_t duration; // milliseconds
            std::string category;
            uint64_t fileSize;
            uint64_t psSize;
            uint64_t footerSize;
            uint64_t readOffset;
            uint64_t readSize;
            std::string header;
            uint32_t numFileRead;
            uint32_t numStripeCache;
        };

        // read path logging methods
        virtual void logRead(const ReadMetrics &metrics) const {
        }

        virtual void logColumnFilter(
            const ColumnFilter &filter,
            uint64_t numColumns,
            uint64_t numNodes,
            bool hasSchema) const {
        }

        // write path logging methods
        virtual void logWrite(size_t size, size_t duration) const {
        }

        virtual void
        logWrite(const std::string &operation, size_t size, size_t duration) const {
        }

        struct StripeFlushMetrics {
            std::string writerVersion;
            uint32_t stripeIndex;
            uint64_t rawStripeSize;
            uint64_t rowsInStripe;
            uint64_t stripeSize;
            uint64_t limit;
            uint64_t maxDictSize;
            uint64_t dictionaryMemory;
            uint64_t outputStreamMemoryEstimate;
            uint64_t availableMemory;
            size_t flushOverhead;
            float compressionRatio;
            float flushOverheadRatio;
            float averageRowSize;
            uint64_t stripeSizeEstimate;
            uint64_t groupSize;
            bool close;
        };

        virtual void logStripeFlush(const StripeFlushMetrics & /* metrics */) const {
        }

        struct FileCloseMetrics {
            std::string writerVersion;
            uint64_t footerLength;
            uint64_t fileSize;
            uint64_t cacheSize;
            uint64_t numCacheBlocks;
            int32_t cacheMode;
            uint64_t numOfStripes;
            uint64_t rowCount;
            uint64_t rawDataSize;
            uint64_t numOfStreams;
            // NOTE: these are memory footprints after the final flush.
            int64_t totalMemory;
            int64_t dictionaryMemory;
            int64_t generalMemory;
        };

        virtual void logFileClose(const FileCloseMetrics & /* metrics */) const {
        }

        static std::shared_ptr<const MetricsLog> voidLog() {
            static std::shared_ptr<const MetricsLog> log{new MetricsLog("")};
            return log;
        }

    protected:
        MetricsLog(const std::string &file) : file_{file} {
        }

        static std::string getMetricTypeName(MetricsType type) {
            switch (type) {
                case MetricsType::HEADER:
                    return "HEADER";
                case MetricsType::FOOTER:
                    return "FOOTER";
                case MetricsType::FILE:
                    return "FILE";
                case MetricsType::STRIPE:
                    return "STRIPE";
                case MetricsType::STRIPE_INDEX:
                    return "STRIPE_INDEX";
                case MetricsType::STRIPE_FOOTER:
                    return "STRIPE_FOOTER";
                case MetricsType::STREAM:
                    return "STREAM";
                case MetricsType::STREAM_BUNDLE:
                    return "STREAM_BUNDLE";
                case MetricsType::GROUP:
                    return "GROUP";
                case MetricsType::BLOCK:
                    return "BLOCK";
                case MetricsType::TEST:
                    return "TEST";
            }
        }

        std::string file_;
    };

    using LogType = MetricsLog::MetricsType;
    using MetricsLogPtr = std::shared_ptr<const MetricsLog>;

    class DwioMetricsLogFactory {
    public:
        virtual ~DwioMetricsLogFactory() = default;

        virtual MetricsLogPtr create(const std::string &filePath, bool ioLogging) = 0;
    };

    void registerMetricsLogFactory(std::shared_ptr<DwioMetricsLogFactory> factory);

    DwioMetricsLogFactory &getMetricsLogFactory();
} // namespace kumo::pollux::dwio::common
